#if UNITY_EDITOR && DEBUG_BEHAVIOUR_TREE
using Devil;
using System.Collections.Generic;
using System.Text;

namespace GameToolkit.BehaviourTree
{
    // 状态更新回调，用以编辑器同步执行状态视图
    internal interface IStateValidator
    {
        event System.Action OnStateChanged;
        bool IsStateObsolute { get; }
        NodeState State { get; }
        void ValidateState();
    }

    public partial class Decoration : IStateValidator
    {
        public event System.Action OnStateChanged;

        // 该节点缓存的条件状态
        NodeState mState;
        bool _obsolute = true;
        public bool IsStateObsolute
        {
            get
            {
                return _obsolute;
            }
            internal set
            {
                if (_obsolute != value)
                {
                    _obsolute = value;
                    if(attachedPlayer != null)
                    {
                        attachedPlayer.ValidateState(this);
                    }
                }
            }
        }
        public NodeState State
        {
            get { return mState; }
            internal set
            {
                var stat = mState;
                mState = value;

                if (stat != mState)
                {
                    _obsolute = false;
                    if (attachedPlayer != null)
                    {
                        attachedPlayer.ValidateState(this);
                    }
                }
            }
        }

        void IStateValidator.ValidateState()
        {
            OnStateChanged?.Invoke();
        }
    }

    public partial class ExecutableEntry : IStateValidator
    {

        public event System.Action OnStateChanged;
        bool _obsolute;
        public bool IsStateObsolute
        {
            get
            {
                return _obsolute;
            }
            internal set
            {
                if (_obsolute != value)
                {
                    _obsolute = value;
                    if(attachedPlayer != null)
                    {
                        attachedPlayer.ValidateState(this);
                    }
                }
            }
        }
        void IStateValidator.ValidateState()
        {
            OnStateChanged?.Invoke();
        }
        internal void AppendTreeInfo(StringBuilder buffer, int deep)
        {
            buffer.AppendTab(deep).Append("Node<").Append(GetType().FullName).Append("> (").Append(guid).Append(')');
            if (mDecorators != null && mDecorators.Length > 0)
            {
                buffer.AppendLine().AppendTab(deep).Append('{');
                for (int i = 0; i < mDecorators.Length; i++)
                {
                    var decor = mDecorators[i];
                    if (decor == null)
                        continue;
                    buffer.AppendLine().AppendTab(deep + 1).Append(decor is Condition ? "Condition" : "Decorator<").Append(decor.GetType().FullName).Append("> (").Append(decor.guid).Append(")");
                }
                buffer.AppendLine().AppendTab(deep).Append('}');
            }
            var pnode = this as IExecutableParent;
            if (pnode != null)
            {
                var size = pnode.ChildCount;
                for (int i = 0; i < size; i++)
                {
                    var child = pnode.GetChild(i);
                    if (child == null)
                        continue;
                    buffer.AppendLine();
                    child.AppendTreeInfo(buffer, deep + 1);
                }
            }
        }
        public override string ToString()
        {
            var buf = ParallelUtils.GetBuilder();
            AppendTreeInfo(buf, 0);
            return ParallelUtils.ReturnRelease(buf);
        }
    }

    // 绑定行为树运行时实例与资源的关联状态
    public partial class BehaviourTreePlayer
    {
        internal static readonly object _global_binding_lock = new object();
        internal static HashSet<BehaviourTreePlayer> sAllPlayers = new HashSet<BehaviourTreePlayer>();

        internal static BehaviourTreePlayer FindPlayer(BehaviourTreeAsset asset)
        {
            lock (_global_binding_lock)
            {
                foreach(var pl in sAllPlayers)
                {
                    if (pl.BindingAsset == asset)
                        return pl;
                }
            }
            return null;
        }


        System.Action<BehaviourTreePlayer> mSubmitState;
        System.Action<BehaviourTreePlayer> mSubPlayerChanged;
        System.Action<BehaviourTreePlayer> mPlayerDestroied;
        internal event System.Action<BehaviourTreePlayer> OnSubmitBehaviourState
        {
            add
            {
                mSubmitState += value;
                _is_binding = true;
            }
            remove
            {
                mSubmitState -= value;
                _is_binding = mSubmitState != null || mSubPlayerChanged != null || mPlayerDestroied != null;
            }
        }
        internal event System.Action<BehaviourTreePlayer> OnSubPlayerChanged
        {
            add
            {
                mSubPlayerChanged += value;
                _is_binding = true;
            }
            remove
            {
                mSubPlayerChanged -= value;
                _is_binding = mSubmitState != null || mSubPlayerChanged != null || mPlayerDestroied != null;
            }
        }
        internal event System.Action<BehaviourTreePlayer> OnPlayerDestroied
        {
            add
            {
                mPlayerDestroied += value;
                _is_binding = true;
            }
            remove
            {
                mPlayerDestroied -= value;
                _is_binding = mSubmitState != null || mSubPlayerChanged != null || mPlayerDestroied != null;
            }
        }

        readonly object _sync_binding_lock;
        bool _is_binding;
        Dictionary<string, object> mGuidMap = new Dictionary<string, object>();
        // 执行 update 的对象
        HashSet<object> mUpdatingNodes;
        // 执行状态更新
        HashSet<IStateValidator> mUpdatingStates;
        HashSet<BehaviourTreePlayer> mSubmitPlayers;

        internal void ValidateState(IStateValidator vali)
        {
            if (!mDisposed && _is_binding)
            {
                lock (_sync_binding_lock)
                {
                    mUpdatingStates.Add(vali);
                    mSubmitPlayers.Add(this);
                }
            }
        }

        internal bool AddActiveNode(object node)
        {
            if (mDisposed || !_is_binding)
                return false;
            lock (_sync_binding_lock)
            {
                bool add = false;
                var it = node as ExecutableEntry;
                if (it == null)
                {
                    add = mUpdatingNodes.Add(node);
                }
                else
                {
                    while (it != null)
                    {
                        if (mUpdatingNodes.Add(it))
                        {
                            add = true;
                            it = it.Parent;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                if (add)
                {
                    mSubmitPlayers.Add(this);
                }
                return add;
            }
        }

        // main thread
        void SubmitBehaviourStates()
        {
            lock (_sync_binding_lock)
            {
                if (!mDisposed && (mUpdatingNodes.Count > 0 || mUpdatingStates.Count > 0))
                {
                    foreach (var vali in mUpdatingStates)
                    {
                        vali.ValidateState();
                    }
                    foreach (var player in mSubmitPlayers)
                    {
                        player.mSubmitState?.Invoke(player);
                    }
                }
                mUpdatingStates.Clear();
                mSubmitPlayers.Clear();
            }
        }

        internal bool IsActiveNode(object node)
        {
            lock (_sync_binding_lock)
            {
                var p = this;
                while (p.mParent != null)
                {
                    p = p.mParent;
                }
                return p.mUpdatingNodes.Contains(node);
            }
        }

        internal object GetInstanceByGuid(string guid)
        {
            object inst;
            if (!string.IsNullOrEmpty(guid) && mGuidMap.TryGetValue(guid, out inst))
                return inst;
            else
                return null;
        }

        void ResetGuidMap()
        {
            mGuidMap.Clear();
            if (mMainLooper != null)
            {
                AddToGuidMap(mMainLooper.Root);
            }
        }

        void AddToGuidMap(ExecutableEntry node)
        {
            if (!string.IsNullOrEmpty(node.guid))
                mGuidMap[node.guid] = node;
            var count = node.DecoratorCount;
            for (int i = 0; i < count; i++)
            {
                var deco = node.GetDecorator(i);
                if (!string.IsNullOrEmpty(deco.guid))
                    mGuidMap[deco.guid] = deco;
            }
            if (node is IExecutableParent cnode)
            {
                count = cnode.ChildCount;
                for (int i = 0; i < count; i++)
                {
                    AddToGuidMap(cnode.GetChild(i));
                }
            }
        }


        void NotifyCreate()
        {
            lock(_global_binding_lock)
            {
                if (!mDisposed)
                    sAllPlayers.Add(this);
            }
        }

        void NotifyDispose()
        {
            lock(_global_binding_lock)
            {
                sAllPlayers.Remove(this);
            }
            mPlayerDestroied?.Invoke(this);
            //SubmitBehaviourStates();
        }

        //[UnityEditor.MenuItem("CONTEXT/BehaviourTreeAsset/Create Runtime")]
        //static void CreateRuntime(UnityEditor.MenuCommand cmd)
        //{
        //    var inst = cmd.context as BehaviourTreeAsset;
        //    ParallelUtils.Schedule((asset) =>
        //    {
        //        var player = new BehaviourTreePlayer(null);
        //        player.SetBehaviourTree(asset);
        //        Debug.Log(player.mMainLooper.Root.ToString());
        //        player.Dispose();
        //    }, inst, false);
        //}
    }
}
#endif